#include "XJ/XJConnection.h"
#include "PacketConstructer.h"
#include "XJ/XJ103Constructer.h"
#include <QtNetwork/QNetworkInterface>
XJConnection::XJConnection()
{
    m_tcpClientSocket = new QTcpSocket;
    m_udpClientSocket = new QUdpSocket;
    QObject::connect(m_tcpClientSocket, &QTcpSocket::readyRead, this, &XJConnection::slot_recvTcpMsg);
    QObject::connect(m_tcpClientSocket, &QTcpSocket::disconnected, this, &XJConnection::slot_socketDisconnected);
    QObject::connect(m_tcpClientSocket, &QTcpSocket::connected, this, &XJConnection::slot_connectSuccess);
    QObject::connect(&m_heartTimer, &QTimer::timeout, this, &XJConnection::slot_sendHeartBeatMsg);
    m_dataSendNo = 0;

    m_timerConnct.setInterval(500);
    m_connectTimeLimit = 10;
    connect(&m_timerConnct, &QTimer::timeout, this, &XJConnection::slot_connectDevice);

    connect(&m_timerSendCmdList, &QTimer::timeout, this, &XJConnection::slot_sendTcpMsgList);

    connect(m_udpClientSocket, &QUdpSocket::readyRead, this, &XJConnection::slot_recvUdpMsg);
}

XJConnection::~XJConnection()
{
    delete m_tcpClientSocket;
   // delete m_server;
}

void XJConnection::setIPPortAndConnect(const QString &destDevIp, const QString &localIp, const QString &port)
{
    m_destDevAddr = destDevIp;
    m_port = port;
    m_localAddr = localIp;
    // 103报文中的 asdu 的cpu服务地址 apci 中占两个字节 0xdc00 ,asdu占一个字节0xdc

    m_destDevAddrLastPart =  Utils::uintStrToHexStr(destDevIp.split('.')[3], 4);
    qDebug() << "cpu服务地址也就是目标设备的ip地址的最后一格字段的16进制数 m_destDevAddrLastPart = "<< m_destDevAddrLastPart;
    m_cpuAddr = Utils::uintStrToHexStr(destDevIp.split('.')[3], 2);
    qDebug() << "目标设备地址的最后一格字段 m_cpuAddr = "<< m_cpuAddr;

    m_localAddrLastPart = Utils::uintStrToHexStr(localIp.split('.')[3],4);
    qDebug() << "源设备地址的最后一格字段4位 m_srcDevAddr = "<< m_localAddrLastPart;

    //连接设备
    if (m_timerConnct.isActive() == false )
    {
        m_connectTimeLimit = 10;        // 重新给十次机会
        m_timerConnct.start(200);
    }
}

void XJConnection::sendTcpMsg(QString &msg)
{
    // 每次调用 发送都将 apci 的消息编号加17,18
    if(m_dataSendNo == 65535)
    {
        m_dataSendNo = 0;
    }

    m_dataSendNo++;
    // 将 数据编号转换成 16进制数字
    QString dataNoStr = Utils::intToHexStr(m_dataSendNo, 4);
    // 修改 msg 的 数据编号， 在报文的下标为 33,34,35,36
    QByteArray msgByte = QByteArray::fromHex( msg.replace(32,4,dataNoStr.toLatin1()).toLatin1());

    m_tcpClientSocket->write(msgByte,msgByte.length());

    emit sig_sendMsg("[发送消息]: " + msgByte.toHex());
}

void XJConnection::sendTcpMsg(QByteArray &msg)
{
    qDebug() << "发送数据编号 = " << m_dataSendNo;
    // 将 数据编号转换成 16进制数字
    QString dataNoStr = Utils::intToHexStr(m_dataSendNo, 4);
    // 修改 msg 的 数据编号， 在报文的下标为 33,34,35,36
//    qDebug() << "tcp 发送消息" << msg.replace(32,4,dataNoStr.toLatin1()) << msg;
    msg[33] = dataNoStr.toLatin1()[0];
    msg[34] = dataNoStr.toLatin1()[1];
    msg[35] = dataNoStr.toLatin1()[2];
    msg[36] = dataNoStr.toLatin1()[3];

    int ret = m_tcpClientSocket->write(QByteArray::fromHex(msg), msg.length());   // 加入写入成功反馈
    if (ret == -1) {
        emit sig_sendMsg("[发送消息error]: "+msg);
        return;
    }

    // 每次调用 发送都将 apci 的消息编号加17,18
    m_dataSendNo++;

    emit sig_sendMsg("[发送消息]: "+msg);
}

void XJConnection::sendTcpMsgI(QByteArray &msg)
{
    qDebug() << "发送数据编号 = " << m_dataSendNo;
    // 将 数据编号转换成 16进制数字

    int ctrl0 = (m_dataSendNo & 0x7f) << 1;
    int ctrl1 = (m_dataSendNo & 0x7f ) >> 7;
    int ctrl2 = (m_dataRecvNo & 0x7f) << 1;
    int ctrl3 = (m_dataRecvNo & 0x7f) <<1;

    QByteArray ctrl0b = Utils::intToHexByte(ctrl0);
    QByteArray ctrl1b = Utils::intToHexByte(ctrl1);
    QByteArray ctrl2b = Utils::intToHexByte(ctrl2);
    QByteArray ctrl3b = Utils::intToHexByte(ctrl3);

    // 修改apci的控制域状态
    msg.replace(4, 2, ctrl0b);
    msg.replace(6, 2, ctrl1b);
    msg.replace(8, 2, ctrl2b);
    msg.replace(10, 2, ctrl3b);

    // 传送报文
    int ret = m_tcpClientSocket->write(QByteArray::fromHex(msg), msg.length());   // 加入写入成功反馈
    if (ret == -1) {
        emit sig_sendMsg("[发送消息error]: "+msg);
        return;
    }

    // 每次调用 发送都将 apci 的消息编号加17,18
    m_dataSendNo++;

    emit sig_sendMsg("[发送消息]: "+msg);
}

void XJConnection::connectToDevice()
{
    quint16 peerPort(m_port.toUInt());

    // socket 使用读写方式打开

    // 设置超时响应
    int count = 5;
    while (count>0)
    {
        m_tcpClientSocket->connectToHost(m_destDevAddr, peerPort, QIODevice::ReadWrite);
        QThread::msleep(100);

        if (m_tcpClientSocket->state() == QAbstractSocket::ConnectedState)
        {
            // 发出来连接成功信号，client界面收到此信号刷新界面控件可否状态
            return;
        }

        count--;
    }

    return;
}

QByteArray XJConnection::getRecvTcpMsg()
{
    return m_recvBuffer;
}

QByteArray XJConnection::getRecvMsg()
{
    return m_recvBuffer;
}

void XJConnection::disconnectFromDevice()
{
    m_tcpClientSocket->disconnectFromHost();
    //m_clientState = 0;
    m_heartTimer.stop();
}

void XJConnection::startHeartTimer()
{
    m_heartTimer.start(1000);
}

void XJConnection::stopHeartTimer()
{
    m_heartTimer.stop();
}

void XJConnection::slot_socketDisconnected()
{
    m_tcpClientSocket->disconnectFromHost();
    emit sig_disconnect();
    m_heartTimer.stop();
}

void XJConnection::slot_recvTcpMsg()
{
    m_recvBuffer = m_tcpClientSocket->readAll();
    // 接收状态计数器，如果接收到的报文不是 不计数的U帧格式， 接收计数器自加
    //判断 ctr0 的控制域确认是否是 I 帧
//    qDebug() << "apic 格式 = "
    int apciType =  ApciFormatJudge(m_recvBuffer);
    if (  apciType == UFrame ) {

    }
    m_dataSendNo++;

    emit sig_readyRead();
}


void XJConnection::slot_connectSuccess()
{
    //m_clientState =1;
    emit sig_conncted();
}

void XJConnection::slot_sendHeartBeatMsg()
{
    // 目标设备地址
    ushort srcAddr = m_localAddrLastPart.toUInt(nullptr,16);
    ushort destAddr = m_destDevAddrLastPart.toUInt(nullptr,16);
    //m_tcpClientSocket->write(QByteArray::fromHex();  //本机地址最后字段 108

    QByteArray m_hearBeatMsg =  m_packetConstructer->constructHeartPacket(srcAddr, destAddr);
    sendTcpMsg(m_hearBeatMsg);
    //    qDebug() << "心跳报文: " << m_packetConstructer->constructHeartPacket(srcAddr, destAddr);
}

void XJConnection::slot_connectDevice()
{
    quint16 peerPort(m_port.toUInt());
    m_tcpClientSocket->connectToHost(m_destDevAddr, peerPort, QIODevice::ReadWrite);
    m_connectTimeLimit--;

    if (m_connectTimeLimit == 0)
    {
        emit sig_sendMsg("连接超时！");
        m_timerConnct.stop();
        m_connectTimeLimit = 10;
        return;
    }

    if (m_tcpClientSocket->state() == QAbstractSocket::ConnectedState)
    {
        // 发出来连接成功信号，client界面收到此信号刷新界面控件可否状态
        emit sig_sendMsg("连接成功。");
        m_timerConnct.stop();
        m_connectTimeLimit = 10;
        return;
    }
}

void XJConnection::displayError(QAbstractSocket::SocketError socketError)
{
    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
         sig_sendMsg( "[错误] : The host was not found. Please check the host name and port settings.");
        break;
    case QAbstractSocket::ConnectionRefusedError:
        sig_sendMsg( "[错误] : The connection was refused by the peer. "
                     "Make sure the fortune server is running, "
                     "and check that the host name and port "
                     "settings are correct.");
        break;
    default:
        sig_sendMsg( "[错误] :" + m_tcpClientSocket->errorString());
    }
}


int XJConnection::ApciFormatJudge(QByteArray msg)
{
    // 从原始报文取出控制域判断
    int ctrl0 = msg.at(2);
    int ctrl2 = msg.at(4);
    // 最低两个位位都是1 ，ctr0位格式: 00 00 00 11， ctr2: 0000000 0
    QString c0 = QString("%1").arg(ctrl0, 8, 2, QLatin1Char('0'));

    QString c2 = QString("%1").arg(ctrl2, 8, 2, QLatin1Char('0'));

    qDebug() << c0 << c2;
    if(c2.at(7)=='0' && c0.at(7)=='1' && c0.at(6)=='1') {
        return 2;
    }
    else if(c2.at(7)=='0' && c0.at(7)=='1' && c0.at(6)=='0'){
        return 1;
    }
    else {
        return 0;
    }
}

QStringList XJConnection::getLocalHostIPList()
{
    QStringList strIpAddress;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    int nListSize = ipAddressesList.size();
    for (int i = 0; i < nListSize; ++i)
    {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
                ipAddressesList.at(i).toIPv4Address()) {
            strIpAddress.append(ipAddressesList.at(i).toString());
        }
    }

    if (strIpAddress.isEmpty())
    {
        strIpAddress.append(QHostAddress(QHostAddress::LocalHost).toString());
    }

    return strIpAddress;
}

void XJConnection::sendUdpMsg(QByteArray &msg)
{
    QHostAddress ip = QHostAddress(m_localAddr);
    m_udpClientSocket->writeDatagram(msg, msg.size(),ip, m_port.toUInt());
}

bool XJConnection::bindUdp()
{
    QHostAddress ip = QHostAddress(m_destDevAddr);
    if (true == m_udpClientSocket->bind(ip, m_port.toUInt())){
        return true;
    }
    return false;
}

void XJConnection::sendTcpMsg(const QByteArrayList &msgList)
{
    m_cmdListReadBill = msgList;
    m_timerSendCmdList.start(XJ_SEND_INTERVEAL);
}

void XJConnection::slot_sendTcpMsgList()
{
    if ( m_indexSendCmdList == m_cmdListReadBill.count())
    {
        m_timerSendCmdList.stop();
        m_indexSendCmdList = 0;
        return;
    }
    QByteArray msg = m_cmdListReadBill.at(m_indexSendCmdList);
    sendTcpMsg(msg);
    m_indexSendCmdList++;
}

void XJConnection::slot_sendUdpMsgList()
{
    if (!bindUdp()){
        emit sig_sendMsg("绑定udp端口错误！");
    }
    if ( m_indexSendCmdList == m_cmdListReadBill.count())
    {
        m_timerSendCmdList.stop();
        m_indexSendCmdList = 0;
        return;
    }
    QByteArray msg = m_cmdListReadBill.at(m_indexSendCmdList);
    sendUdpMsg(msg);
    m_indexSendCmdList++;
}

void XJConnection::slot_recvUdpMsg()
{
    m_recvBuffer = m_tcpClientSocket->readAll();
    emit sig_readyRead();
}
